/**
 * 1. new Promise时，需要传递一个 executor 执行器，执行器立刻执行
 * 2. executor 接受两个参数，分别是 resolve 和 reject
 * 3. promise 只能从 pending 到 rejected, 或者从 pending 到 fulfilled
 * 4. promise 的状态一旦确认，就不会再改变
 * 5. promise 都有 then 方法，then 接收两个参数，分别是 promise 成功的回调 onFulfilled, 
 *      和 promise 失败的回调 onRejected
 * 6. 如果调用 then 时，promise已经成功，则执行 onFulfilled，并将promise的值作为参数传递进去。
 *      如果promise已经失败，那么执行 onRejected, 并将 promise 失败的原因作为参数传递进去。
 *      如果promise的状态是pending，需要将onFulfilled和onRejected函数存放起来，等待状态确定后，再依次将对应的函数执行(发布订阅)
 * 7. then 的参数 onFulfilled 和 onRejected 可以缺省
 * 8. promise 可以then多次，promise 的then 方法返回一个 promise
 * 9. 如果 then 返回的是一个结果，那么就会把这个结果作为参数，传递给下一个then的成功的回调(onFulfilled)
 * 10. 如果 then 中抛出了异常，那么就会把这个异常作为参数，传递给下一个then的失败的回调(onRejected)
 * 11.如果 then 返回的是一个promise,那么需要等这个promise，那么会等这个promise执行完，promise如果成功，
 *   就走下一个then的成功，如果失败，就走下一个then的失败
 */

/**
 * PromiseA+ 2.1
 */
enum Status {
  PENDING = "pending",
  FULFILLED = "fulfilled",
  REJECTED = 'rejected'
}

type Executor = (resolve: (value: unknown) => unknown, reject: (reason: unknown) => unknown) => unknown
type Reject = (reason: unknown) => unknown
type Resolve = (value: unknown) => unknown

class Promise_ {
  private status: Status = Status.PENDING
  private value: unknown
  private reason: unknown
  private onFulfilled:Function[] = []
  private onRejected:Function[] = []



  constructor(executor: Executor) {
    executor(this.resolve.bind(this), this.reject.bind(this))
  }

  /**
   * PromiseA+ 2.1.2
   * @param value 
   */
  resolve(value: unknown) {
    if (this.status === Status.PENDING) {
      this.status = Status.FULFILLED
      this.value = value
      this.onFulfilled.forEach(fn => fn())
    }
  }

    /**
   * PromiseA+ 2.1.3
   * @param value 
   */
  reject(reason: unknown) {
    if (this.status === Status.PENDING) {
      this.status = Status.REJECTED
      this.reason = reason
      this.onRejected.forEach(fn => fn())
    }
  }


  /**
   * PromiseA+ 2.2
   * @param onFulfilled 
   * @param onRejected 
   */
  then(onFulfilled: (value: unknown) => unknown, onRejected: (reason: unknown) => unknown) {
    /**
     * PromiseA+ 2.2.7
     */
    const p = new Promise_((resolve, reject) => {

      resolve = resolve.bind(p)
      reject = reject.bind(p)
      switch(this.status) {
        case Status.FULFILLED: {
          // PromiseA+ 2.2.7.1
          //PromiseA+ 2.2.4 ---微任务 setTimeout
          setTimeout(() => {
            try {
              const res = onFulfilled(this.value)
              resolvePromise(p, res, resolve, reject)
            } catch (e) {
              reject(p)
            }
          })
          break;
        }
        case Status.REJECTED: {
          setTimeout(() => {
            try {
              const res = onRejected(this.reason)
              resolvePromise(p, res, resolve, reject)
            } catch(e) {
              reject(p)
            }
          })
          break;
        }
        case Status.PENDING: {
          this.onFulfilled.push(() => {
            setTimeout(() => {
              try {
                let x = onFulfilled(this.value)
                resolvePromise(p, x, resolve, reject)
              } catch(e) {
                reject(p)
              }
            })
          })

          this.onRejected.push(() => {
            setTimeout(() => {
              try {
                let x = onRejected(this.reason)
                resolvePromise(p, x, resolve, reject)
              } catch(p) {
                reject(p)
              }
            })
          })
        }
      }
    })

    return p
  }
}

function resolvePromise(promise2: unknown, x: any, resolve: Resolve, reject: Reject) {
  if (promise2 === x) reject(new TypeError('Chaining cycle'))
  if (x && typeof x === 'object' || typeof x === 'function') {
    let used = false;
    try {
      let then = x.then;
      if (typeof then === 'function') {
        then.call(x, (y: unknown) => {
          if (used) return;
          used = true;
          resolvePromise(promise2, y, resolve, reject)
        }, (r: unknown) => {
          if (used) return;
          used = true;
          reject(r)
        })
      } else {
        if (used) return;
        used = true
        resolve(x)
      }
    } catch(e) {
      if (used)return;
      used = true;
      reject(e)
    }
  } else {
    resolve(x)
  }
}


const a = new Promise_((resolve, reject) => {
  setTimeout(() => {
    resolve('1sad')
  }, 2000)
}).then(res => {
  console.log('resolve', res)
  return 2
}, reason => {
  console.log('reject', reason)
}).then(res =>{
  console.log(2)
}, e => {})

// export default {}